<script setup lang="ts">
import useFullscreen from '../hooks/useFullscreen';
import type { CSSProperties } from 'vue';
import { offset } from '../utils';

defineOptions({
    inheritAttrs: false
});

const props = defineProps([
    'width',
    'height',
    'visible',
    'mousePosition',
    'visibleChange',
    'closable',
    'fullscreen',
    'title',
    'footer',
    'class',
    'fullscreenId',
    'bodyHeight',
    'zIndex',
    'footerCenter'
]);
const emits = defineEmits(['cancel', 'fullscreen']);

const transformOrigin = ref<string>();
const contentStyleRef = computed(() => {
    const { width, height } = props;
    const contentStyle: CSSProperties = {};
    if (width !== undefined) {
        contentStyle.width = typeof width === 'number' ? `${width}px` : width;
    }
    if (height !== undefined) {
        contentStyle.height = typeof height === 'number' ? `${height}px` : height;
    }
    if (transformOrigin.value) {
        contentStyle.transformOrigin = transformOrigin.value;
    }
    if (transformScale.value) {
        contentStyle.transform = transformScale.value;
    }
    return contentStyle;
});

/**
 * 动画进入开始前钩子
 */
const oldTransformOrigin = ref();
const onPrepare = () => {
    nextTick(() => {
        if (dialogRef.value && props.visible) {
            const elementOffset = offset(dialogRef.value);
            transformOrigin.value = props.mousePosition
                ? `${props.mousePosition.x - elementOffset.left}px ${
                      props.mousePosition.y - elementOffset.top
                  }px`
                : '';
            oldTransformOrigin.value = transformOrigin.value;
        }
    });
};

/**
 * 动画离开前, 还原动画位置，防止全屏操作影响动画位置偏离
 */
const onPreLeave = () => {
    transformOrigin.value = oldTransformOrigin.value;
};

/**
 * 动画结束后钩子
 */
const onVisibleChanged = (visible: boolean) => {
    props.visibleChange(visible);
};

/**
 * 处理全屏过渡动画
 */
const fullVisible = ref(false);
const transformScale = ref();
const handleFullscreenAnimate = () => {
    transformOrigin.value = 'center';
    fullVisible.value = true;

    nextTick(() => {
        fullVisible.value = false;
    });
};

const { expand, handleFullscreen } = useFullscreen();
const onFullscreen = () => {
    handleFullscreen(`#m-dialog__${props.zIndex}`);
    if (!expand.value) handleFullscreenAnimate();
    nextTick(() => {
        emits('fullscreen', expand.value);
    });
};

const dialogRef = ref<HTMLDivElement>();
</script>

<template>
    <Transition
        name="q-zoom"
        enterFromClass="q-zoom-enter"
        enterActiveClass="q-zoom-enter q-zoom-enter-prepare"
        enterToClass="q-zoom-enter q-zoom-enter-active"
        leaveFromClass="q-zoom-leave"
        leaveActiveClass="q-zoom-leave q-zoom-leave-active"
        leaveToClass="q-zoom-leave q-zoom-leave-active"
        :onBeforeEnter="onPrepare"
        :onBeforeLeave="onPreLeave"
        :onAfterEnter="() => onVisibleChanged(true)"
        :onAfterLeave="() => onVisibleChanged(false)"
        appear
    >
        <div
            v-show="visible && !fullVisible"
            ref="dialogRef"
            :style="[contentStyleRef, $attrs.style as CSSProperties]"
            :class="['m-modal', props.class]"
        >
            <div class="m-modal__content">
                <q-icon
                    v-if="fullscreen"
                    :icon-class="!expand ? 'fangda1' : 'suoxiao1'"
                    size="16px"
                    class="u-modal__icon u-modal__fullscreen"
                    @click="onFullscreen"
                ></q-icon>
                <q-icon
                    v-if="!closable"
                    icon-class="guanbi1"
                    color="#434343"
                    size="16px"
                    class="u-modal__icon u-modal__close"
                    @click="emits('cancel')"
                ></q-icon>
                <header v-if="title" class="m-modal__header">
                    <slot name="header"></slot>
                </header>
                <main class="m-modal__body" :style="{ height: props.bodyHeight }">
                    <slot></slot>
                </main>
                <Transition
                    enterFromClass="fade-enter-from"
                    enterActiveClass="fade-enter-active"
                    enterToClass="fade-enter-to"
                >
                    <footer
                        v-if="footer"
                        :class="['m-modal__footer', { 'is-center': footerCenter }]"
                    >
                        <slot name="footer"></slot>
                    </footer>
                </Transition>
            </div>
        </div>
    </Transition>
</template>

<style lang="scss" scoped>
@include b(min-modal) {
    .m-modal__header {
        padding: 12px 24px;
    }

    .u-modal__close {
        padding: 12px;
        margin: 0;
    }
}

@include b(m-modal) {
    position: relative;
    top: 100px;
    width: auto;
    padding: 0 0 24px;
    margin: 0 auto;

    @include e(content) {
        background-color: var(--q-bg-color);
    }

    &.m-modal-confirm {
        margin-top: 50px;

        .m-modal__content {
            border-radius: 4px;
        }

        .m-modal__header {
            padding: 10px 24px;
            border-bottom: none;
        }

        .m-modal__body {
            padding-bottom: 16px;
        }

        .u-modal__icon {
            top: 8px;
            right: 8px;
            font-size: 12px !important;
        }
    }

    @include e(header) {
        padding: 16px 24px;
        font-size: 16px;
        font-weight: bold;
        line-height: 16px;
        border-bottom: 1px solid var(--q-modal-border-color);
    }

    @include e(body) {
        padding: 24px;
        overflow: auto;

        @include scrollbar_y;
        @include scrollbar_hover;
    }

    @include e(footer) {
        padding: 12px 16px;
        text-align: right;
        background: transparent;
        border-top: 1px solid var(--q-modal-border-color);
        border-radius: 0 0 2px 2px;

        @include when(center) {
            text-align: center;
        }
    }
}

@include b(u-modal) {
    @include e(icon) {
        position: absolute;
        padding: 8px;
        color: var(--q-text-color-regular);
        cursor: pointer;
    }

    @include e(fullscreen) {
        right: 38px;
        padding: 16px;
        font-size: 18px;
        @include pseudo(hover) {
            color: var(--q-color-primary) !important;
        }
    }

    @include e(close) {
        right: 3px;
        padding: 10px;
        margin: 6px;
        transition: transform 0.2s cubic-bezier(0.785, 0.135, 0.15, 0.86) 0s;

        @include pseudo(hover) {
            color: var(--q-color-primary) !important;
            transform: rotate(180deg);
        }
    }
}

/* modal zoom */
.q-zoom-enter {
    animation-duration: 0.3s;
    animation-fill-mode: both;
    animation-play-state: paused;
}

.q-zoom-leave {
    animation-duration: 0.3s;
    animation-fill-mode: both;
    animation-play-state: paused;
}

.q-zoom-enter.q-zoom-enter-active {
    animation-name: zoom-in;
    animation-play-state: running;
}

.q-zoom-leave.q-zoom-leave-active {
    pointer-events: none;
    animation-name: zoom-out;
    animation-play-state: running;
}

.q-zoom-enter {
    opacity: 0;
    transform: scale(0);
    animation-timing-function: cubic-bezier(0.08, 0.82, 0.17, 1);
}

.q-zoom-enter-prepare {
    transform: none;
}

.q-zoom-leave {
    animation-timing-function: cubic-bezier(0.78, 0.14, 0.15, 0.86);
}

@keyframes zoom-in {
    0% {
        opacity: 0;
        transform: scale(0.2);
    }

    100% {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes zoom-out {
    0% {
        transform: scale(1);
    }

    100% {
        opacity: 0;
        transform: scale(0.2);
    }
}
</style>
